/**********************************************************************
*  Copyright (c) 2007 - Victor Jacobs - victor.jacobs@gmail.com
*
*  Permission is hereby granted, free of charge, to any person
*  obtaining a copy of this software and associated documentation
*  files (the "Software"), to deal in the Software without
*  restriction, including without limitation the rights to use,
*  copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the
*  Software is furnished to do so, subject to the following
*  conditions:
*
*  The above copyright notice and this permission notice shall be
*  included in all copies or substantial portions of the Software.
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
*  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
*  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
**********************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Vj.MathLibrary;

namespace Vj.GraphLibrary
{
    public class Graph
    {
        private List<Node> nodes;

        public List<Node> Nodes
        {
            get { return nodes; }
        }

        public Node this[int index]
        {
            get
            { return nodes[index]; }
            set
            { nodes[index] = value; }
        }

        private float connectionProb;

        public float ConnectionProbability
        {
            get { return connectionProb; }
            set { connectionProb = value; }
        }

        private Vector dimensions;

        public Vector Dimensions
        {
            get { return dimensions; }
            set { dimensions = value; }
        }
        
        public Graph(int numberOfNodes, float connectionProbablity, Vector dimensions)
        {
            nodes = new List<Node>();

            this.connectionProb = connectionProbablity;
            this.dimensions = dimensions;

            CreateNodes(numberOfNodes);
            CreateConnections();
        }

        private void CreateNodes(int count)
        {
            Node node;

            for (int i = 0; i < count; i++)
            {
                node = new Node();
                
                if (dimensions.Length == 2)
                {
                    node.Location = new Vector(RandomGrabber.Next(-dimensions.X / 2.0, dimensions.X / 2.0),
                                               RandomGrabber.Next(-dimensions.Y / 2.0, dimensions.Y / 2.0));
                }
                else if (dimensions.Length == 3)
                {
                    node.Location = new Vector(RandomGrabber.Next(-dimensions.X / 2.0, dimensions.X / 2.0),
                                               RandomGrabber.Next(-dimensions.Y / 2.0, dimensions.Y / 2.0),
                                               RandomGrabber.Next(-dimensions.Z / 2.0, dimensions.Z / 2.0));
                }
                else
                    throw new InvalidOperationException("Only 2D and 3D graphs currently supported.");

                
                nodes.Add(node);
            }
        }

        private void CreateConnections()
        {
            Connection connection;

            for (int i = 0; i < nodes.Count; i++)
            {
                for (int j = 0; j < nodes.Count; j++)
                {
                    if (RandomGrabber.NextDouble() <= (double)connectionProb)
                    {
                        connection = new Connection(nodes[i], nodes[j]);

                        nodes[i].Connections.Add(connection);
                        nodes[j].Connections.Add(connection);
                    }
                }
            }
        }

        public void ArrangeGraph()
        {
            /*
            set up initial node velocities to (0,0)
            set up initial node positions randomly // make sure no 2 nodes are in exactly the same position
            loop
                total_kinetic_energy := 0 // running sum of total kinetic energy over all particles
                for each node
                    net_force := (0, 0) // running sum of total force on this particular node
                     
                    for each other node
                        net_force := net_force + Coulomb_repulsion( this_node, other_node )
                    next node
                     
                    for each spring connected to this node
                        net_force := net_force + Hooke_attraction( this_node, spring )
                    next spring
                     
                    // without damping, it moves forever
                    this_node.velocity := (this_node.velocity + timestep * net_force) * damping
                    this_node.position := this_node.position + timestep * this_node.velocity
                    total_kinetic_energy := total_kinetic_energy + this_node.mass * (this_node.velocity)^2
                next node
            until total_kinetic_energy is less than some small number  //the simulation has stopped moving
            */

          
            //for (int i = 0; i < graph.Layers.Count; i++)
            //{
            //    for (int j = 0; j < graph.Layers[j].Nodes.Count; j++)
            //    {
            //        graph.Layers[i].Nodes[j].Velocity = new PointF(0.0f, 0.0f);
            //        graph.Layers[i].Nodes[j].Location = new Point(rg.Next(0, 200), rg.Next(0, 200));
            //    }            
            //}



            //double totalEnergy = 1.0;
            //double minimalEnergy = 0.1;

            //while (totalEnergy > minimalEnergy)
            //{
            //    totalEnergy = 0.0;


            //    for (int i = 0; i < graph.Layers.Count; i++)
            //    {
            //        for (int j = 0; j < graph.Layers[j].Nodes.Count; j++)
            //        {
            //            //graph.Layers[i].Nodes[j].Velocity = new PointF(0.0f, 0.0f);
            //            //graph.Layers[i].Nodes[j].Location = new Point(rg.Next(0, 200), rg.Next(0, 200));

            //            PointF netForce = new PointF(0.0f, 0.0f);

            //            for (int m = 0; m < graph.Layers.Count; m++)
            //            {
            //                for (int n = 0; n < graph.Layers[m].Nodes.Count; n++)
            //                {
            //                    netForce.X = netForce.X + Repulsion(graph.Layers[i].Nodes[j], graph.Layers[m].Nodes[n]).X;
            //                    netForce.Y = netForce.Y + Repulsion(graph.Layers[i].Nodes[j], graph.Layers[m].Nodes[n]).Y;
            //                }
            //            }







            //        }
            //    }

            //}

        }


        private Vector Repulsion(Node node1, Node node2)
        {
            //double xDiff = (double)(node2.Location.X - node1.Location.X) * (node2.Location.X - node1.Location.X);
            //double yDiff = (double)(node2.Location.Y - node1.Location.Y) * (node2.Location.Y - node1.Location.Y);
            //double dist = Math.Sqrt(xDiff + yDiff);

            //float xForce = 10.0f / (float)Math.Sqrt((node2.Location.X - node1.Location.X) * (node2.Location.X - node1.Location.X));
            //float yForce = 10.0f / (float)Math.Sqrt((node2.Location.Y - node1.Location.Y) * (node2.Location.Y - node1.Location.Y));

            //return new PointF(xForce, yForce);
            return null;
        }

        public void ResetGraph()
        {
              this.CreateNodes(this.nodes.Count);
              this.CreateConnections();
        }

    }
}
